package org.chen.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.chen.constants.ImageStatus;
import org.chen.exception.StorageException;
import org.chen.model.ImageTask;
import org.chen.model.sys.ImageResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.*;
import java.util.Date;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

// 文件系统存储实现
@Service
@Slf4j
public class FileSystemStorageService {
    private final Path imagesLocation;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private AsyncTaskExecutor taskExecutor;

    private final BlockingQueue<ImageTask> imageQueue = new LinkedBlockingQueue<>();

    @PostConstruct
    public void init() {
        startImageProcessingWorkers(5); // 启动5个工作线程
    }

    private void startImageProcessingWorkers(int workerCount) {
        for (int i = 0; i < workerCount; i++) {
            taskExecutor.execute(this::processImageQueue);
        }
    }

    private void processImageQueue() {
        while (true) {
            try {
                ImageTask task = imageQueue.take();
                processImage(task);
            } catch (Exception e) {
                log.error("Image processing error", e);
            }
        }
    }

    public FileSystemStorageService(ConfigServiceImpl configService) {
        String uploadPath = configService.getConfigValue("article.crawler.upload-path");
        this.imagesLocation = Paths.get(uploadPath);
        createStorageDirectory();
    }

    // 新增：保存爬虫图片并记录资源信息
    public void saveCrawlerImage(String imageUrl, String fileName) {
        // 先创建记录，状态设为处理中
        ImageResource resource = new ImageResource();
        resource.setFileName(fileName);
        resource.setType("CRAWLER");
        resource.setUrl(imageUrl);
        resource.setOriginalName(getFileNameFromUrl(imageUrl));
        resource.setStatus(ImageStatus.PROCESSING);
        resource.setCreateTime(new Date());
        resource.setPath(imagesLocation + "/" + fileName); // 设置路径
        resource.setIsDeleted(false);
        resource.setArticleId(null);

        // 保存记录并获取ID
        ImageResource savedResource = mongoTemplate.save(resource);

        // 添加到处理队列
        imageQueue.offer(new ImageTask(imageUrl, fileName, savedResource.getId()));
    }

    private void processImage(ImageTask task) {
        try {
            Path imagePath = downloadAndSaveImage(task.getImageUrl(), task.getFileName());

            // 更新文件信息
            String md5 = calculateMd5(imagePath);
            long size = Files.size(imagePath);

            // 更新记录
            Query query = Query.query(Criteria.where("id").is(task.getResourceId()));
            Update update = new Update()
                    .set("status", ImageStatus.COMPLETED)
                    .set("updateTime", new Date())
                    .set("md5", md5)
                    .set("size", size);

            mongoTemplate.updateFirst(query, update, ImageResource.class);

        } catch (Exception e) {
            log.error("Failed to process image: {}", task.getImageUrl(), e);
            updateImageStatus(task.getResourceId(), ImageStatus.FAILED, e.getMessage());
        }
    }

    private void updateImageStatus(String resourceId, ImageStatus status, String errorMessage) {
        Query query = Query.query(Criteria.where("id").is(resourceId));
        Update update = Update.update("status", status)
                .set("updateTime", new Date());
        if (errorMessage != null) {
            update.set("errorMessage", errorMessage);
        }
        mongoTemplate.updateFirst(query, update, ImageResource.class);
    }

    // 辅助方法
    private String calculateMd5(Path filePath) throws IOException {
        try (InputStream is = Files.newInputStream(filePath)) {
            return DigestUtils.md5DigestAsHex(is);
        }
    }

    private String getFileNameFromUrl(String url) {
        return url.substring(url.lastIndexOf('/') + 1);
    }

    private void createStorageDirectory() {
        try {
            if (!Files.exists(imagesLocation)) {
                Files.createDirectories(imagesLocation);
            }
        } catch (IOException e) {
            throw new StorageException("Could not initialize storage", e);
        }
    }

    // 下载并保存图片，返回保存的文件路径
    public Path downloadAndSaveImage(String imageUrl, String fileName) {
        try {
            Path destinationFile = imagesLocation.resolve(fileName)
                    .normalize()
                    .toAbsolutePath();
            validatePath(destinationFile);

            URL url = new URL(imageUrl);
            try (InputStream in = url.openStream()) {
                Files.copy(in, destinationFile, StandardCopyOption.REPLACE_EXISTING);
            }
            return destinationFile;
        } catch (Exception e) {
            throw new StorageException("Failed to download image", e);
        }
    }

    private void validatePath(Path destinationFile) {
        if (!destinationFile.getParent().startsWith(imagesLocation)) {
            throw new StorageException("Cannot store file outside current directory.");
        }
    }

    // 删除文件
    public void delete(String filename) {
        try {
            Path file = imagesLocation.resolve(filename).normalize();
            Files.deleteIfExists(file);
        } catch (IOException e) {
            throw new StorageException("Failed to delete file " + filename, e);
        }
    }
}
